<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>12.6组件事件与emit实现</title>
</head>
<body>
  <div id="app"></div>
  
<script type="module">

import {createRenderer} from '../3.renderer/createRenderer.js'
import {reactive, shallowReactive, shallowReadonly} from '../2.observer/reactive.js'
import {ref, proxyRefs, toRefs} from '../2.observer/ref.js'
import {effect} from '../2.observer/effect.js'
import {queueJob} from '../3.renderer/queueJob.js'
import {resolveProps} from './fns/resolveProps.js'
import {renderContext} from './fns/renderContext.js'
import {hasPropsChanged} from './fns/hasPropsChanged.js'
//3.组件
const MyComponent = {
  props: {
    title: String
  },
  setup(props, {emit}) {
    emit('testEmit', 'arguments name')
    const count = ref(11)
    return {
      count
    }
   
  },
  render() {
    return {
      type: 'div',
      children: 'count is ' + this.count + this.title
    }
  }
}
const parentComponent = {
  name: 'parentComponent',
  data() {
    return {
      title: 'A Big Title'
    }
  },
  render() {
    return {
      key:1,
      type: 'div',    
      children:[
        {
          key: 2,
          type: MyComponent,
          props: {
            title:this.title,
            id: 1,
            onTestEmit:(name)=>{
              console.log(name)
            }
          },
        },
        {
          key: 3,
          type: 'p',
          children: 'pppp',
          props: {
            onClick:()=> {
              this.title = 'new-title '
              console.log(this.title, 'this.title')
              // vnode.children[0].props.title = 'A Small Title'
            }
          }
        }
      ],
    }
  }
}
const vnode = {
  type: parentComponent
}
function debuggerMountComponent(patch) {
  return function mountComponent (vnode, container, anchor) {
    const componentOptions = vnode.type
    const {render, data = ()=>({}), props:propsOption, setup= ()=>({}), beforeCreate, created, beforeMount, mounted, beforeUpdate, updated} = componentOptions
    beforeCreate?.()
    const state = reactive(data())
    const [prop, attr] = resolveProps(propsOption, vnode.props)
    //组件实例
    const instance = {
      state,
      isMounted: false,
      subTree: null,
      props: shallowReactive(prop)
    }
    function emit(eventName, ...payload) {
      const handlerName = 'on' + eventName[0].toUpperCase() + eventName.slice(1)
      console.log(handlerName)
      const handler = instance.props[handlerName]
      if(typeof handler === 'function') {
        handler(...payload)
      } else {
        console.error('事件不存在')
      }
    }
    const setupContext = {attr, emit}
    const setupResult = setup(shallowReadonly(instance.props), setupContext)
    let setupState
    if(typeof setupResult === 'object') {
      setupState = proxyRefs(setupResult)
    } else {
      if(render) console.error('setup 函数返回渲染函数， render选项将被忽略')
      setupState = setupResult()
    }
    vnode.component = instance
    const context = renderContext(instance, setupState)
    created?.call(context)
    effect(()=> {
      const subTree = render.call(context, state)
      if(!instance.isMounted) {
        beforeMount?.call(context)
        patch(null, subTree, container, anchor)
        instance.isMounted = true
        mounted?.call(context)
      } else {
        beforeUpdate?.call(context)
        patch(instance.subTree, subTree, container, anchor)
        updated?.call(context)
      }
      instance.subTree = subTree
    }) 
  }
}

function debuggerPatchComponent() {
  return function patchComponent(n1, n2, container) {
    const instance = (n2.component = n1.component)
    const {props} = instance
    if(hasPropsChanged(n1.props, n2.props)) {
      const [nextProps] = resolveProps(n2.type.props, n2.props)
      for(const k in nextProps) {
        props[k] = nextProps[k]
      }
      for(const k in props) {
        if(!k in nextProps) delete props[k]
      }
    }
  }
}


const render = createRenderer({debuggerMountComponent, debuggerPatchComponent})
render(vnode, document.getElementById('app'))
</script>
</body>

</html>